#include "Classe.h"

#include <algorithm>
#include "ATL.h"

using namespace std;

const unsigned int Classe::Zero = 0;

const string Classe::Caracteristiques[6] =
  {
    "Force       ",
    "Dextérité   ",
    "Constitution",
    "Intelligence",
    "Sagesse     ",
    "Charisme    "
  };

DebugLevelType Classe::elementaire  = INFO;
DebugLevelType Classe::fonction     = INFO;

/** Constructeur par défaut.
 * Ce constructeur ne devrait pas être appelé, mais pour être sûr, je
 * l'ai quand même implémenté.
 *
 * Il crée une Classe "fonctionnelle" dans le sens que n'importe
 * quelle méthode appelée sur une instance créée par ce moyen devrait
 * s'exécuter sans erreur.
 */
Classe::Classe()
  : XP( 1 ), Type_race( "Humain" )
{
  for(unsigned int i(0); i < 7; i++)
    Traits[i] = 0;

  ptrComp         = 0;
  ptrRes          = 0;
  ptrNbrComp      = 0;
  ptrNbrRes       = 0;
  ptrNomEnsComp   = 0;
  ptrNbrEns       = &Zero;
  ptrMaxEns       = &Zero;
  ptrTraitsRequis = 0;
  ptrRacesAdmises = 0;

  Niveau = 0;
  Niveau_mag = 0;
  TC.push_back(Toutes);
}

/**
 * Constructeur standard. Est utilisé lors de la création d'une Classe
 * en mode console ou graphique. Initialise les pointeurs et fixe les
 * traits et le TypeClasse. La liste des compétences accessibles est
 * également mise à jour.
 *
 * @param xp Nombre de pt d'expérience du personnage
 * @param trait tableau des traits du personnage
 * @param tc type de classe (càd Combattant, Mage, Prêtre ou Roublard)
 * du personnage
 * @param race du personnage.
 */
Classe::Classe(unsigned int xp, unsigned int trait[7], TypeClasse tc,
	       const string& race)
  : XP(xp), Type_race( race )
{
  for(unsigned int i(0); i < 7; i++)
    Traits[i] = trait[i];

  ptrComp         = 0;
  ptrRes          = 0;
  ptrNbrComp      = 0;
  ptrNbrRes       = 0;
  ptrNomEnsComp   = 0;
  ptrNbrEns       = &Zero;
  ptrMaxEns       = &Zero;
  ptrTraitsRequis = 0;
  ptrRacesAdmises = 0;

  Niveau = 0;
  Niveau_mag = 0;
  TC.push_back(Toutes);
  TC.push_back(tc);
}

/**
 * Constructeur de copie, réalise la copie profonde.
 *
 * @param classe instance à copier
 */
Classe::Classe(const Classe& classe)
{
  ptrComp         = classe.ptrComp;
  ptrRes          = classe.ptrRes;
  ptrNbrComp      = classe.ptrNbrComp;
  ptrNbrRes       = classe.ptrNbrRes;
  ptrNomEnsComp   = classe.ptrNomEnsComp;
  ptrNbrEns       = classe.ptrNbrEns;
  ptrMaxEns       = classe.ptrMaxEns;
  ptrTraitsRequis = classe.ptrTraitsRequis;
  ptrRacesAdmises = classe.ptrRacesAdmises;

  Type_classe     = classe.Type_classe;
  Type_race       = classe.Type_race;

  XP = classe.XP;
  for(unsigned int i(0); i<7; i++)
    Traits[i] = classe.Traits[i];
  Niveau = classe.Niveau;
  Niveau_mag = classe.Niveau_mag;
  Comp_classe.assign( classe.Comp_classe.begin(), classe.Comp_classe.end() );
  TC.assign( classe.TC . begin(), classe.TC . end() );
}

/** Destructeur.
 *
 */
Classe::~Classe()
{}

/**
 * Sert à afficher le niveau du personnage (si le personnage est
 * multi-classé, affiche le niveau de la Classe).
 */
void Classe::affiche() const
{
  blank() << "Niveau : " << Niveau << endreq;
}

/**
 * Nouvelle méthode, qui s'occupe de déterminer\n 1) le niveau\n 2) le
 * niveau de magie\n 3) le TAC0.
 *
 * @return le TAC0
 */
unsigned int Classe::calculeNombres()
{
  calculNiveau();
  calculNivMag();

  return calculeTAc0();
}

/**
 * Affiche un récapitulatif de la classe comprenant les traits, les
 * restrictions et les compétences. Fait appel à
 * afficheCompetences().
 *
 * Le niveau de sortie est paramétré par Classe::elementaire :\n
 * \e FUNC permet d'afficher le nom au début et à la fin de la
 * méthode.
 *
 * @param os flot sur lequel les information seront écrites, par
 * défaut blank()
 */
void Classe::afficherStats(ostream& os)
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::afficherStats -> début" << endreq;

  os << "Classe : " << Type_classe << endreq;

  for (unsigned int i(0); i < 6; i++)
    os << Caracteristiques[i] << ' ' << Traits[i] << endreq;

  afficheCompetences(os);

  func() << "Classe::afficherStats -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Sert à déterminer les compétences de classe du personnage. Est
 * appelé lors de la lecture d'un fichier xml.
 *
 * @param capa est le vecteur qui contient les compétences
 */
void Classe::setCompetences(const vector<unsigned int> capa)
{
  Comp_classe.assign( capa . begin(), capa . end() );
}

/**
 * Affiche les compétences de classe.  Est destimé à faire un
 * récapitulatif des compétences de classe, lors de l'utilisation du
 * soft en mode console. Permet également de récupérer les information
 * pour un Aperçu du personnage (console ou graphique).
 *
 * Fait appel à getCompetence(), pour pouvoir gérer de manière simple
 * les restrictions et les compétences associées à un pourcentage.
 *
 * Le niveau de sortie est paramétré par Classe::fonction :\n
 * \e FUNC permet d'afficher le nom au début et à la fin de la
 * méthode.\n
 * \e DEBUG permet d'afficher le nombre de compétences.
 *
 * @param os flot, blank() est utilisé par défaut
 */
void Classe::afficheCompetences(ostream& os) const
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::afficheCompetences -> début" << endreq;
  
  os << "Récapitulation des compétences de classe : " << endreq;
  unsigned int taille( getCombien() );
  string comp_str;
  bool pourcent( false );
  
  debug() << "Nbr de comp : " << taille << endreq;
  
  for (unsigned int i(0); i < taille; i++)
    {
      comp_str = getCompetence(i, pourcent);
      os << " " << comp_str << endreq;
      if ( pourcent )
	i++;
    }
  
  func() << "Classe::afficheCompetences -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Permet de retrouver le nom de la Classe.
 *
 * @return le nom de la classe ("Guerrier", "Voleur", ...)
 */
string Classe::getClasse() const
{
  return Type_classe;
}

/**
 * Donne le nombre courant de pts d'expérience.
 *
 * @return nombre de pts d'expérience
 */
unsigned int Classe::getXP() const
{
  return XP;
}

/**
 * Permet de changer la valeur d'un trait \e après la création de la
 * classe.
 *
 * @param i numéro du trait
 * @param val nouvelle valeur du trait
 */
void Classe::setTrait(const unsigned int& i, const unsigned int& val)
{
  if ( i < 7 )
    Traits[ i ] = val;
}

/**
 * Récupère le trait désiré (y compris le score de force expectionnel.
 *
 * @param i indice du trait voulu.
 * @return la valeur du trait voulu (unsigned int), ou 999 en cas
 * d'erreur.
 */
unsigned int Classe::getTrait(const unsigned int& i) const
{
  if (i < 7)
    return Traits[i];
  else
    return 999;
}

/**
 * Donne le nombre de compétences.
 *
 * @return taille du conteneur Comp_classe, si une (ou plusieurs)
 * compétence est associée à un pourcentage, ce nombre n'est pas le
 * nombre de compétences.
 */
unsigned int Classe::getCombien() const
{
  return Comp_classe.size();
}

/**
 * Retourne un compétence sous forme d'une string.
 *
 * @param i numéro de la compétence
 * @return nom de la compétence i, ainsi que l'éventuel pourcentage
 * associé.
 */
unsigned int Classe::getComp(const unsigned int& i) const
{
  if (i < Comp_classe.size())
    return Comp_classe[i];
  else
    return 255;
}

/**
 * Permet de récupérer le nom de la compétence donnée, avec l'éventuel
 * pourcentage associé. Les scores évoluant avec le niveau tels que
 * l'\e Attaque \e sournoise du Voleur, \e Neutralisation du Rôdeur, etc, sont
 * également gérées ici.
 *
 * La méthode gère également les compétences bizarres, mais je ne suis
 * pas sûr qu'elle peut gérer comme il le faut le pourcentage associé
 * à un talent de Voleur prit comme compétence de Mage... il me semble
 * que oui.
 *
 * Le niveau de sortie est paramétré par Classe::fonction :\n
 * \e FUNC affiche le nom au début et à la fin de la méthode.\n
 * \e DEBUG affiche le numéro de la compétence.\n
 *
 * @param i numéro de la compétence désirée.
 * @param sauter permet de savoir s'il faut sauter le prochain nombre
 * rencontré, lors de l'écriture des compétences
 * @param LaTeX permet de (dés)activer le mode LaTeX pour l'affichage
 * des accents et du signe "%". Par défaut, la sortie est en mode non
 * LaTeX
 * @return le nom de la compétence, suivi par le pourcentage si existant
 */
string Classe::getCompetence(const unsigned int& i, bool& sauter,
			     const bool& LaTeX) const
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::getCompetence -> début" << endreq;

  debug() << "Compétence numéro " << i << " : " << Comp_classe[i];
  
  ListComp tmp;

  sauter = false;

  if ( Comp_classe[i] >= *ptrNbrComp &&
       Comp_classe[i] < *ptrNbrRes + *ptrNbrComp )
    {
      debug() << " => Restriction" << endreq;
      tmp = ptrRes -> at( Comp_classe[i] - *ptrNbrComp );
    }
  else if ( Comp_classe[i] < *ptrNbrComp )
    {
      debug() << " => Compétence classique" << endreq;
      tmp =  ptrComp -> at( Comp_classe[i] ) ;
    }
  else
    {
      debug() << " => Compétence \"à tiroir\"" << endreq;
      /*
       * On va chercher à quel "ensemble" cette compétence appartient,
       * et comme ça on aura :
       * 1) le pointeur sur le conteneur
       * 2) l'index de la compétence dans ce conteneur.
       */
      unsigned int j( i - 1 );
      do
	{
	  tmp = ptrComp -> at( Comp_classe[ j ] );
	}
      while ( !tmp.aSousCategorie() );

      const vector< ListComp > *ptr;
      unsigned int aBase( tmp.adresseBase );
      ptr = tmp.sousCategorie;

      tmp = ptr -> at( Comp_classe[i] - aBase );
    }

  if (tmp.Pourcentage)
    {
      func() << "Classe::getCompetence -> fin" << endreq;
      Debug::depileNiveau();
      if ( LaTeX )
	return tmp.Comp + " " + ecrireString(getComp(i+1)) + " \\%";
      else 
	return afficheAccent(tmp.Comp) + ' '  + ecrireString(getComp(i+1))
	  + " %";
      sauter = true;
    }
  else if (tmp.AutreScore == Basique)
    {
      func() << "Classe::getCompetence -> fin" << endreq;
      Debug::depileNiveau();
      if ( LaTeX )
	return tmp.Comp + " " + getScoreAdditionnel( Comp_classe[i] );
      else
	return afficheAccent(tmp.Comp) + ' '  +
	  getScoreAdditionnel( Comp_classe[i] );
    }
  else if (tmp.AutreScore == Pourcentage)
    {
      func() << "Classe::getCompetence -> fin" << endreq;
      Debug::depileNiveau();
      if ( LaTeX )
	return tmp.Comp + " " + getScoreAdditionnel( Comp_classe[i] ) + " \\%";
      else
	return afficheAccent(tmp.Comp) + ' '  +
	  getScoreAdditionnel( Comp_classe[i] ) + " %";
    }
  else
    {
      func() << "Classe::getCompetence -> fin" << endreq;
      Debug::depileNiveau();
      if ( LaTeX )
	return tmp.Comp;
      else
	return afficheAccent(tmp.Comp);
    }
}

/**
 * Permet de récupérer la valeur de base du pourcentage associé
 * à une compétence.
 *
 * @param i numéro de la compétence.
 * @return valeur de base de la compétence.
 */
unsigned int Classe::getValeurBase(const unsigned int& i) const
{
  if ( ptrComp -> at(i) . Pourcentage )
    return  ptrComp -> at(i) . valeurBase;
  else
    return 0;
}

/**
 * Permet de récupérer le niveau du personnage (qui n'est pas accessible
 * depuis l'extérieur).
 *
 * @return niveau du personage (pour la classe considérée).
 */
unsigned int Classe::getNiveau() const
{
  return Niveau;
}


/**
 * Permet de récupérer le niveau de magie du personnage (qui n'est pas
 * accessible depuis l'extérieur).
 *
 * @return niveau du personage (pour la classe considérée).
 */
unsigned int Classe::getNiveauMagie() const
{
  return Niveau_mag;
}

/**
 * Réalise l'écriture du livre de sort dans la feuille de
 * personnage. Il s'agit ici du traitement générique, donc on ne fait
 * rien.
 *
 * Le niveau de sortie est paramétré par Classe::fonction :\n
 * \e FUNC affiche les messages de début et de fin de méthode.\n
 * \e DEBUG affiche le message "Rien à faire".
 *
 * @param nom_fichier nom du fichier à éditer.
 * @param ecrase permet d'ouvrir le fichier en écrasant le contenu
 * existant (\e true) ou en ajoutant les lignes après la dernière ligne
 * actuelle (\e false).
 */
void Classe::ecrireSorts(const string& nom_fichier, const bool& ecrase) const
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::ecrireSort -> début" << endreq;

  debug() << "Rien à faire" << endl << flush;
  ofstream sortie;
  if (ecrase)
    sortie.open(nom_fichier.c_str());
  else
    sortie.open(nom_fichier.c_str(), ios::app);
  sortie << "%"  << getClasse() << endl << "% rien à dire" << endl;
  sortie.close();

  func() << "Classe::ecrireSort -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Permet de récupérer le nombre de sort de chaque niveau que la
 * Classe peut lancer.
 *
 * Ici la méthode ne fait rien, la gestion est spécifique à la classe.
 *
 * Le niveau de sortie est paramétré par Classe::fonction :\n
 * \e FUNC affiche les messages de début et de fin de méthode.
 *
 * @param sorts tableau contenant le nombre de sort de chaque niveau
 * disponibles, est édité par la méthode.
 * @param taille correspond au nombre de niveaux de sort accessibles.
 */
void Classe::nbrSort(unsigned int sorts[], const unsigned int& taille) const
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::nbrSort -> début" << endreq;

  if ( taille > 0 )
    {
      warning() << "Pas de sorts pour la classe " << getClasse() << endreq;
      for (unsigned int i(0); i < taille; i++)
	sorts[i] = 0;
    }

  func() << "Classe::nbrSort -> fin" << endreq;
}

/**
 * Sert à virer les accents dans le nom des écoles de magies et
 * sphères (écrit en syntaxe LaTeX pour plus de logique).
 *
 * Supprime en même temps le 2ème nom de l'école (càd Illusion -
 * Fantasme => Illusion).

 * Le niveau de sortie est paramétré par Classe::elementaire :\n
 * \e FUNC affiche le nom au début et à la fin de la méthode.\n
 * \e LFUNC permet d'afficher l'évolution de la chaîne de caractère
 * lors du traitement des accents.\n
 * \e DEBUG affiche l'évolution lors de la troncature.
 *
 * @param brut string comprenant des accents en syntaxe Latex et / ou
 * des espaces.
 * @return string sans accents ni espaces
*/
string Classe::supprimeAccents(const string& brut) const
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::supprimeAccents -> début" << endreq;


  string retour(brut);
  size_t loc(retour.find("\\'", 0));
  while (loc != string::npos)
    {
      lfunc() << retour << ' ' << loc << endreq;
      retour.replace(loc, 2, "");
      loc = retour.find("\\'", 0);
      lfunc() << retour << endreq;
    }

  loc = retour.find("\\ -", 0);
  if (loc != string::npos)
    {
      // ici il faut tronquer le nom
      debug() << retour << " -> ";
      retour.replace(loc, retour.size() - loc, "");
      debug() << retour << endreq;
    }

  loc = retour.find(" -", 0);
  if (loc != string::npos)
    {
      // ici il faut tronquer le nom
      debug() << retour << " -> ";
      retour.replace(loc, retour.size() - loc, "");
      debug() << retour << endreq;
    }
    
  func() << "Classe::supprimeAccents -> fin" << endreq;
  Debug::depileNiveau();
  return retour;
}

/**
 * Retourne le TypeClasse du personnage.
 *
 * @return TC, le type de classe du personnage.
 */
const vector<TypeClasse>* Classe::getTC() const
{
  return &TC;
}

/**
 * Faire progresser le personnage, en augmentant son nombre de pts
 * d'expérience.
 *
 * @param xp le nombre de pts d'expérience gagnés.
 */
void Classe::augmenterXP(const unsigned int& xp)
{
  XP += xp;
  calculNiveau();
}

/**
 * Gestion des niveau de sortie des fonctions. Est appelée par
 * lireNiveauxSortie().
 *
 * @param level le niveau (INFO, DEBUG, ...).
 * @param n permet de sélectionner quel variable est affectée.
 */
void Classe::setLevel(const DebugLevelType& level, const unsigned int& n)
{
  switch(n)
    {
    case 1:
      elementaire = level;
      break;
    case 2:
      fonction = level;
      break;
    default:
      warning() << "Variable inconnue, dans Classe::setLevel " << n << endreq;
    }
}

/**
 * Permet de remplir son Livre de sorts à partir des écoles
 * accessibles.  L'ajout est sécurisé (impossible de prendre deux fois
 * le sort).
 *
 * La méthode est spécifique à chaque classe ayant un livre de
 * sort. Cette méthode ne fait donc rien.
 *
 * Le niveau de sortie est paramétré par Classe::fonction :\n
 * \e FUNC affiche le message "rien à faire".
 */
void Classe::ajoutSorts()
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::ajoutSorts : on est dans Classe, rien à faire ici"
	 << endreq; 
  Debug::depileNiveau();
}

/**
 * Ajoute des sorts à la classe. On vérifie d'abord que la classe en
 * a, puis on ajoute les nouveaux et on trie le tout.
 *
 * Le niveau de sortie est paramétré par Classe::fonction :\n
 * \e FUNC permet d'afficher le nom au début et à la fin de la
 * méthode.\n
 * \e DEBUG affiche le nombre de sort qui seront ajoutés.
 *
 * @param vec le conteneur des nouveaux sorts
 */
void Classe::ajoutSorts(const vector<Sort>& vec)
{
  Debug::empileNiveau( &fonction );
  func() << "Classe ajoutSorts -> début" << endreq;

  if (Niveau_mag > 0)
    {
      debug() << vec.size() << " sort à ajouter" << endreq;
      Cl_sorts.insert(Cl_sorts.end(), vec.begin(), vec.end());

      vector<Sort>::iterator Begin, End, it;
      Begin = Cl_sorts.begin();
      End   = Cl_sorts.end();

      sort(Begin, End);

      unique(Begin, End);
    }
  else
    debug() << "Rien à faire, niveau magique nul" << endreq;
  
  func() << "Classe ajoutSorts -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Est utilisé lors de la lecture d'un personnage à partir d'un
 * fichier XML.  Elle permet de recréer le Livre de sorts du
 * personnage.
 *
 *  Le niveau de sortie est paramétré par Classe::elementaire :\n
 * \e FUNC affiche le nom au début et à la fin de la méthode.\n
 * \e DEBUG affiche le nombre de sort qui seront copiés.
 *
 * @param sorts conteneur des sorts connus par le personnage.
 */
void Classe::setSorts(const vector<Sort>& sorts)
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe setSorts -> début" << endreq;

  debug() << "Nbr de sort à copier : " << sorts.size() << endreq;

  Cl_sorts.assign( sorts.begin(), sorts.end() );

  func() << "Classe setSorts -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Est utilisé pour fixer le nombre de xp juste après la création (si
 * le personnage n'est pas niveau 1 lors de la création)
 */
void Classe::setXP(const unsigned int& xp)
{
  XP = xp;

  calculNiveau();

  calculNivMag();
}

/**
 * Donne le nombre de sorts que le personnage connait.
 *
 * @return taille du containeur Cl_Sorts
 */
unsigned int Classe::getCombienSorts() const
{
  if (Niveau_mag > 0)
    return this -> Cl_sorts.size();
  else
    return 0;
}


/**
 * Applique les ajustements raciaux aux compétences de classe. Ne
 * touche que les talents de voleur et de barde.
 *
 * La méthode affiche un message d'erreur si elle est appelée pour un
 * Voleur ou un Barde.
 *
 * Le niveau de sortie est paramétré par Classe::elementaire :\n
 * \e FUNC affiche les messages de début et de fin de méthode.
 */
void Classe::appliquerModif()
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::appliquerModif -> début" << endreq;

  if ( Type_classe == "Barde" || Type_classe == "Voleur" )
    warning() << Type_classe << " a sa propre méthode, ici on ne fait rien!"
	      << endreq;

  func() << "Classe::appliquerModif -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Permet d'augmenter les talent associés à un pourcentage (Voleurs,
 * Bardes). Ne fait rien dans la cas générique.
 *
 * Le niveau de sortie est paramétré par Classe::elementaire :\n
 * \e FUNC affiche les messages de début et de fin de méthode.\n
 * \e INFO affiche un message de félicitations en cas de passage de
 * niveau.
 *
 * @param lvl_up \e true lors d'un passage de niveau (valeur par
 * défaut), \e false est utilisé lors de la création du personnage.
 */
void Classe::augmenterTalents(const bool& lvl_up)
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::augmenterTalent< -> début" << endreq;

  if ( lvl_up )
    info() << "Félicitation pour le niveau gagné" << endreq;

  func() << "Classe::augmenterTalents -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Permet de récupérer le conteneur des sorts de la classe (pour
 * magiciens et bardes uniquement).
 *
 * @return un pointeur sur le conteneur (const vector<Sort>*).
 */
const vector<Sort>* Classe::getSortAdresse() const
{
  if (getCombienSorts() > 0)
    return const_cast<const vector<Sort>*>( &(this -> Cl_sorts) );
  else
    return 0;
}

/**
 * Permet de récupérer les compétences et restrictions de la classe,
 * ainsi que leurs nombres respectifs.
 *
 * Est utilisé dans construitClasse, ainsi que dans la classe
 * ConstruitClasse de l'interface graphique.
 *
 * @param nbrComp nombre de compétences (écrit par la méthode).
 * @param comp    conteneur des compétences (écrit par la méthode).
 * @param nbrRes  nombre de restrictions (écrit par la méthode).
 * @param restric conteneur des restrictions (écrit par la méthode).
 */
void Classe::getCompetences(unsigned int& nbrComp,
			    vector<ListComp>& comp,
			    unsigned int& nbrRes,
			    vector<ListComp>& restric) const
{
  nbrComp = *ptrNbrComp;
  nbrRes  = *ptrNbrRes;

  comp.assign( ptrComp -> begin(), ptrComp -> end());

  if (ptrRes != 0)
    restric.assign( ptrRes -> begin(), ptrRes -> end());
  else
    restric.clear();
}

/**
 * Permet de récupérer le nombre d'ensembles de capacités raciales,
 * ainsi que le nombre maximal de ces capacités raciales. Sert pour
 * les spécialistes, avec une possible extension au Clercs, Druides.
 *
 * @param nbr nombre d'ensembles différents.
 * @param max nombre maximal de capacités dans un ensemble.
 *
 * @todo changer le nom.
 */
void Classe::getEnsemblesCompetences(unsigned int& nbr, unsigned int& max) const
{
  nbr = *ptrNbrEns;
  max = *ptrMaxEns;
}

/**
 * Permet de récupérer l'ensemble de capacités raciales désiré.
 *
 * @param nbr numéro de l'ensemble voulu
 * @return pointeur sur la première capacité de l'ensemble, ici toujours
 * nul, car uniquement certaines classes possèdent de tels ensembles.
 *
 * @todo renvoyer le pointeur sur le conteneur plutôt que sur le
 * premier élément. (Dans Classe ça ne change rien).
 */
const vector<unsigned int>* Classe::getEnsembleCompetences(const unsigned int& nbr)
  const
{
  if ( nbr > 0 )
    warning() << "Pas d'ensemble de compétences pour " << getClasse()
	      << endreq;
  return 0;
}

// /*
//  * Permet de récupérer de nom de l'ensemble de capacités raciales désiré.
//  *
//  * @param nbr numéro de l'ensemble voulu
//  * @return pointeur sur le nom de l'ensemble voulu
//  *
//  * @todo supprimer cette méthode, et utiliser getEnsemble
//  */
// const string* Classe::getNomEnsemble(const unsigned int& nbr) const
// {
//   if ( ptrNomEnsComp == 0 )
//     {
//       warning() << "Pas d'ensemble de compétences pour " << getClasse()
// 		<< endreq;
//       return 0;
//     }
//   else
//     {
//       if ( nbr > *ptrNbrComp )
// 	{
// 	  warning() << "Tentative d'accéder à un ensemble inexistant"
// 		    << endreq;
// 	  return 0;
// 	}
//       return &( ptrNomEnsComp -> at( nbr ) . Capa );
//     }
   
// }

// /*
//  * Permet de récupérer de prix de l'ensemble de capacités raciales désiré.
//  *
//  * @param nbr numéro de l'ensemble voulu
//  * @return pointeur sur le prix de l'ensemble voulu
//  *
//  * @todo supprimer cette méthode, et utiliser getEnsemble
//  */
// const unsigned int* Classe::getPrixEnsemble(const unsigned int& nbr) const
// {
//   if ( ptrNomEnsComp == 0 )
//     {
//       warning() << "Pas d'ensemble de compétences pour " << getClasse()
// 		<< endreq;
//       return 0;
//     }
//   else
//     {
//       if ( nbr > *ptrNbrComp )
// 	{
// 	  warning() << "Tentative d'accéder à un ensemble inexistant"
// 		    << endreq;
// 	  return 0;
// 	}
//       return &( ptrNomEnsComp -> at( nbr ) . Cout );
//     }
   
// }

/**
 * Donne accès directement à l'ensemble voulu, sous forme de ListCapa,
 * pour pouvoir en extraire le nom et le coût.
 *
 * @param nbr numéro de l'ensemble (index dans la liste), une
 * vérification est effectuée sur la validité de la valeur de nbr.
 * @return un pointeur sur la ListCapa désirée, ou un pointeur nul
 * dans le cas où l'index est invalide ou qu'il n'existe pas
 * d'ensembles de compétences pour cette classe.
 */
const ListCapa* Classe::getEnsemble(const unsigned int& nbr) const
{
   if ( ptrNomEnsComp == 0 )
    {
      warning() << "Pas d'ensemble de compétences pour " << getClasse()
		<< endreq;
      return 0;
    }
  else
    {
      if ( nbr > *ptrNbrComp )
	{
	  warning() << "Tentative d'accéder à un ensemble inexistant"
		    << endreq;
	  return 0;
	}
      return &( ptrNomEnsComp -> at( nbr ) );
    }
}

/**
 * Permet de récupérer la liste complète des ensembles de compétences,
 * avec leur coût respectif.
 *
 * @return un pointeur sur le conteneur des ensembles, ou un pointeur
 * nul s'il n'existe pas de tels ensembles.
 */
const vector<ListCapa>* Classe::getEnsembles() const
{
  return ptrNomEnsComp;
}

/**
 * Retourne le sort désiré. En cas d'erreur, le sort retourné n'existe
 * pas (niveau 0 inexistant).
 *
 * @param i numéro du sort
 * @return le sort numéro i
 */
Sort Classe::getSort(const unsigned int& i) const
{
  if (i < getCombienSorts())
    return Cl_sorts[i];
  else
    return Sort(0, 0, 0);
}

/**
 * Permet de savoir combien de PP sont alloués lors de la création
 * d'une classe donnée.
 *
 * @return le nombre de PP alloués.
 */
unsigned int Classe::getPP() const
{
  if (Type_classe == "Guerrier")
    return 15;
  else if (Type_classe == "Paladin" || Type_classe == "Rôdeur")
    return 60;
  else if (Type_classe == "Clerc")
    return 120;
  else if (Type_classe == "Druide")
    return 100;
  else if (Type_classe == "Mage")
    return 40;
  else if (Type_classe == "Spécialiste")
    return 30;
  else if (Type_classe == "Barde")
    return 70;
  else if (Type_classe == "Voleur")
    return 80;
  else
    return 0;
}

/**
 * Retrouve les compétences et restrictions de la classe. Les numéros
 * de ces compétences sont retournées.
 *
 * @param nbr nombre de compétences possédées.
 * @return pointeur sur le conteneur des compétences possédées.
 */
const vector<unsigned int>* Classe::getToutesCompetences(unsigned int& nbr)
  const
{
  nbr = getCombien();

  return &Comp_classe;
}

/**
 * Retourne la compétences désirée. Si le numéro de compétence demandé
 * n'existe pas, on renvoie une compétence vide.
 *
 * @param i numéro de la compétence désirée.
 * @return compétence voulue.
 */
ListComp Classe::getCompetenceNumero(const unsigned int& i) const
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::getCompetenceNumero -> début" << endreq;

  debug() << "On recherche le numéro " << i << endreq;

  if ( ptrComp == 0 )
    {
      func() << "Classe::getCompetenceNumero -> fin" << endreq;
      Debug::depileNiveau();
      return ListComp();
    }
  else
    {
      func() << "Classe::getCompetenceNumero -> fin" << endreq;
      Debug::depileNiveau();
      return ( ptrComp -> at( i ) );
    }
}

/**
 * Permet de savoir si la classe possède des compétences associées à
 * un pourcentage.
 *
 * @return \e true si oui, \e false sinon. 
 */
bool Classe::possedePourcent() const
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::possedePourcent -> début" << endreq;

  unsigned int taille(getCombien()), numero;

  for (unsigned int i(0); i < taille; i++)
    {
      numero = getComp( i );
      debug() << "Courante : " << numero << endreq;
      if ( numero >= *ptrNbrComp ) continue;
      if ( ptrComp -> at( numero ) . Pourcentage )
	{
	  func() << "Classe::possedePourcent -> fin" << endreq;
	  Debug::depileNiveau();
	  return true;
	}
    }

  func() << "Classe::possedePourcent -> fin" << endreq;
  Debug::depileNiveau();
  return false;
}

/**
 * Donne le nombre de point à dépenser pour l'amélioration des
 * compétences associées à un pourcentage. Sert pour la création et le
 * passage de niveau.
 *
 * @param lvlUp doit être \e true si l'on est en train de passer un
 * niveau (valeur par défaut) et \e false lors de la création.
 * @return le nombre de points à dépenser.
 */
unsigned int Classe::getPourcent(const bool& lvlUp) const
{
  if ( lvlUp )
    info() << "Félicitation pour le niveau gagné!" << endreq;
  return 0;
}

/**
 * Permet de savoir si la classe a un score de Force
 * Exceptionnelle. Les Combattants ont un tel score naturellement, les
 * Clercs peuvent en avoir un à condition de prendre la capacité ad
 * hoc.
 *
 * @return \e true si oui, \e false sinon.
 */
bool Classe::forceExceptionnelle() const
{
  if ( ( getClasse() == "Guerrier" || getClasse() == "Paladin"
	 || getClasse() == "Rôdeur" ) && Traits[0] == 18 )
    return true;
  else
    return false;
}

/**
 * Permet de récupérer le score associé à une compétence, lorsque
 * celui-ci dépend uniquement du niveau (et / ou d'un trait). Pour une
 * Classe générique, il n'existe pas de tels traits, donc rien n'est
 * fait ici.
 *
 * @param i numéro de la compétence
 * @return la chaîne de caractères correspondante, ici toujours "".
 */
string Classe::getScoreAdditionnel(const unsigned int& i) const
{
  Debug::empileNiveau( &fonction );
  func() << "Classe::getScoreAdditionnel -> début" << endreq;

  warning() << "Pas de score additionnel pour " << getClasse() << endreq;

  if ( i > *ptrNbrComp )
    warning() << "Et en plus cette compétence n'existe pas" << endreq;

  func() << "Classe::getScoreAdditionnel -> fin" << endreq;
  Debug::depileNiveau();

  return "";
}

/**
 * Fait le boulot des anciens constructeurs des différentes classes :
 * permet la construction de la Classe de A à Z, on choisit les
 * restrictions et les compétences. Seul le spécialiste a un
 * traitement spécial, vu que l'on peut ôter des compétences (écoles
 * en opposition) déjà prises.
 *
 * @param pps nombre de PP, qui est réutilisé dans la suite de la
 * séquence.
 */
void Classe::construitClasse(unsigned int& pps)
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::construitClasse -> début" << endreq;
  unsigned int nbrEns, nbrCapaEns, i, choix, confirmation, savePP(pps),
    nbrElements;
  vector< unsigned int > tmpComp, tmpRes;
  const vector<unsigned int> *liste = 0;
  const ListCapa *ensemble = 0;
  ListComp tmp;

  /*
   * D'abord on regarde si il y a un ensemble de compétences
   * raciales. Ces ensembles permettent de gérer les différents type
   * de Spécialistes par exemple.
   */
  getEnsemblesCompetences(nbrEns, nbrCapaEns);

  if (nbrEns > 0)
    /*
     * Alors, on a bel et bien des ensembles de compétences. On
     * commence par les afficher toutes.
     */
    {
      blank() << "Sélectionner l'ensemble de compétence de classe :" << endreq;
      for (i = 0; i < nbrEns; i++)
	{
	  ensemble = getEnsemble( i );
	  if ( ensemble -> Cout == 0)
	    blank() << i + 1 << ' ' << ensemble -> Capa << endreq;
	  else
	    blank() << i + 1 << ' ' << ensemble -> Capa
		    << " (pour " << ensemble -> Cout << " pp)" << endreq;
	}
      blank() << ++i << " Aucun" << endreq;
      do
	{
	  choix = demanderUnsignedBorne( "(" + ecrireString( pps ) +
					 " pp) Choix : ", 1, nbrEns + 1 );
	  if ( pps < ensemble -> Cout )
	    {
	      warning() << "Pas assez de pp" << endreq;
	      choix = i;
	    }
	  else
	    pps -= ensemble -> Cout;
	  blank() << "Ensemble sélectionné : "
		  << getEnsemble( choix - 1 ) -> Capa << endreq;
	    
	  confirmation =  demanderUnsignedBorne( "Entrer 1 pour confirmer, 2 pour annuler : ", 1, 2 );
	}
      while ( confirmation == 2 );

      if ( choix != i + 1 )
	{
	  liste =  getEnsembleCompetences( choix - 1 );
	  // Ici il faut ajouter les compétences...
	  for (i = 0; i < nbrCapaEns; i++)
	    {
	      if ( liste -> at( i ) == 0 )
		break;
	      tmpComp.push_back( liste -> at( i ) - 1 );
	    }
	}
    }

  info() << *ptrNbrComp << " compétences et " << *ptrNbrRes << " restrictions"
	 << endreq;

  /*
   * Ici on s'occupe des éventuelles restrictions de la classe
   */
  if ( *ptrNbrRes > 0 )
    {
      savePP = pps;
      blank() << "Sélectionner les restrictions choisies :" << endreq;
      for (i = 0; i < *ptrNbrRes; i++)
	{
	  blank() << i + 1 << ' ' << (ptrRes -> at( i )) . Comp
		  << " (pour " << (ptrRes -> at( i )) . Cout << " pp)"
		  << endreq;
	}
      blank() << *ptrNbrRes + 1 << " Aucune" << endreq;

      do
	{
	  pps = savePP;
	  tmpRes.clear();
	  do
	    {
	      choix = demanderUnsignedBorne( "(" + ecrireString( pps ) +
					     " pp) Choix : ",
					     1, *ptrNbrRes + 1 );
	      if ( choix != *ptrNbrRes + 1 )
		{
		  blank() << "Restriction sélectionnée : "
			  << ptrRes -> at( choix - 1 ) . Comp << endreq;
		  tmpRes.push_back( *ptrNbrComp + choix - 1 );
		  pps += (ptrRes -> at( choix - 1 )) . Cout;
		}
	    }
	  while ( choix != *ptrNbrRes + 1 );
	  nbrElements = tmpRes.size();
	  if ( nbrElements == 0 )
	    blank() << endl << "Aucune restriction sélectionée" << endreq;
	  else
	    {
	      blank () << "Récapitulation des restrictions :" << endl;
	      for (i = 0; i < nbrElements; i++)
		blank() << ptrRes -> at( tmpRes[ i ] - *ptrNbrComp ) . Comp
			<< endl;
	      blank() << endreq;
	    }
	  confirmation = demanderUnsignedBorne( "Entrer 1 pour confirmer, 2 pour annuler : ", 1, 2 );
	}
      while ( confirmation == 2 );

      Comp_classe.assign( tmpComp.begin(), tmpComp.end() );
    }

  // On passe aux compétences
  savePP = pps;
  blank() << "Séléctionner les compétences choisies :" << endreq;
  for (i = 0; i < *ptrNbrComp; i++)
    {
      blank() << i + 1 << ' ' << (ptrComp -> at( i )) . Comp
	      << " (pour " << (ptrComp -> at( i )) . Cout << " pp)"
	      << endreq;
    }
  blank() << *ptrNbrComp + 1 << " Aucune" << endreq;
  
  do
    {
      pps = savePP;
      tmpComp.clear();
      do
	{
	  choix = demanderUnsignedBorne( "(" + ecrireString( pps ) +
					 " pp) Choix : ", 1, *ptrNbrComp + 1 );
	  if ( choix != *ptrNbrComp + 1 )
	    {
	      if ( pps >= (ptrComp -> at( choix - 1 )) . Cout )
		{
		  blank() << "Compétence sélectionnée : "
			  << ptrComp -> at( choix - 1 ) . Comp << endreq;
		  tmpComp.push_back( choix - 1 );
		  if ( ptrComp -> at( choix - 1 ) . Pourcentage )
		    tmpComp.push_back( ptrComp -> at( choix - 1 ).valeurBase );
		  pps -= (ptrComp -> at( choix - 1 )) . Cout;
		}
	      else
		warning() << "Pas assez de pp" << endl;
	    }
	}
      while ( choix != *ptrNbrComp + 1 );
      nbrElements = tmpComp.size();
      if ( nbrElements == 0 )
	blank() << endl << "Aucune compétence sélectionée" << endreq;
      else
	{
	  blank () << "Récapitulation des Compétences :" << endl;
	  for (i = 0; i < nbrElements; i++)
	    {
	      tmp = ptrComp -> at( tmpComp[i] );
	      blank() << tmp. Comp;
	      if ( tmp. Pourcentage )
		{
		  blank() << ' ' << tmp . valeurBase << " %";
		  i++;
		}
	      blank()<< endl;
	    }
	  blank() << endreq;
	}
      confirmation = demanderUnsignedBorne( "Entrer 1 pour confirmer, 2 pour annuler : ", 1, 2 );
    }
  while ( confirmation == 2 );

  // Il faut encore ajouter la détermination des scores à pourcentage

  Comp_classe.insert( Comp_classe.begin(), tmpRes.begin(), tmpRes.end() );
  Comp_classe.insert( Comp_classe.end(), tmpComp.begin(), tmpComp.end() );

//   vector< unsigned int >::const_iterator Begin( Comp_classe.begin() ),
//     End( Comp_classe.end() ), it;

  EcrireElementConteneur< unsigned int > ecrireUInt( &blank() );
  for_each( Comp_classe.begin(), Comp_classe.end(), ecrireUInt );
//   for (it = Begin; it != End; it++)
//     blank() << *it << ' ';
  blank() << endreq;
  
  func() << "Classe::construitClasse -> fin" << endreq;
  Debug::depileNiveau();
}

/**
 * Donne accès au nombre d'ensembles de compétences.
 *
 * @return un pointeur sur le nombre d'ensembles.
 */
const unsigned int* Classe::getNbrEns() const
{
  return ptrNbrEns;
}


/**
 * Donne accès au nombre maximal de compétences dans un ensemble.
 *
 * @return un pointeur sur le nombre max de compétences.
 */
const unsigned int* Classe::getMaxEns() const
{
  return ptrMaxEns;
}

/**
 * Vérifie que l'on peut bien construire la Classe voulue. On vérifie
 * que les traits remplissent les prérequis et on vérifie également
 * que la race est compatible.
 *
 * @param race nom de la Race (càd Elfe, Demi-Ogre, ...).
 * @param ensemble numéro de l'ensemble racial désiré (est en fait
 * utile pour les Spécialistes uniquement).
 * @param complexe utilisé uniquement pour les Spécialistes.
 * @return StatusCode, \e true si tout est OK, \e false sinon. Le
 * message contient une brève explication.
 */
StatusCode Classe::prerequisOK(const string& race,
			       const unsigned int& ensemble,
			       const bool& complexe)
{
  Debug::empileNiveau( &elementaire );
  func() << "Classe::prerequisOK -> " << endreq;

  if ( complexe || ensemble != 99 )
    debug() << "Rien à faire de spécial pour " << Type_classe << endreq;

  if ( ptrRacesAdmises != 0 &&
       ptrRacesAdmises -> find( race ) == ptrRacesAdmises -> end() )
    // Alors on n'a pas le droit!
    {
      error() << "Un " << race << " ne peut pas être " << Type_classe << endreq;
      
      func() << "Classe::prerequisOK -> " << endreq;
      Debug::depileNiveau();

      return StatusCode( false, 0, 0, race + " interdit pour " + Type_classe );
    }

  StatusCode sc;

  if ( ptrTraitsRequis != 0 )
    {
      vector< Prerequis >::const_iterator it, Begin( ptrTraitsRequis -> begin() ),
	End( ptrTraitsRequis -> end() );

      for (it = Begin; it != End; it++)
	{
	  sc = it -> estOK( Traits );
	  if ( ! sc )
	    {
	      error() << Caracteristiques[ sc.valeur() ] << " doit être au minimum "
		      << sc.code() << ", valeur courante : " << Traits[ sc.valeur() ]
		      << endreq;

	      func() << "Classe::prerequisOK -> " << endreq;
	      Debug::depileNiveau();
	      return sc;
	    }
	}
    }

  func() << "Classe::prerequisOK -> " << endreq;
  Debug::depileNiveau();
  return sc;
}
